package com.eastedu.report.principal.assemble;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.deepoove.poi.data.*;
import com.eastedu.common.enums.DateCodeType;
import com.eastedu.common.enums.Section;
import com.eastedu.common.enums.SubjectType;
import com.eastedu.report.common.model.parameter.ReportGenerateParameter;
import com.eastedu.report.common.model.po.CommonDimensionEntity;
import com.eastedu.report.common.model.po.TeacherTeachingActivityResourceUsagePeriodPO;
import com.eastedu.report.common.model.po.TeacherTeachingActivityResourceUsagePeriodSubjectPO;
import com.eastedu.report.common.util.NumberFormatUtil;
import com.eastedu.report.common.util.SqlUtil;
import com.eastedu.report.mapper.TeacherTeachingActivityPrepareResourceUsagePeriodMapper;
import com.eastedu.report.mapper.TeacherTeachingActivityResourceUsageMapper;
import com.eastedu.report.principal.converter.PrincipalTeachingActivityConverter;
import com.eastedu.report.principal.model.teacher.prepare.TeacherPrepareReceiveReportData;
import com.eastedu.report.principal.model.teacher.prepare.TeacherPrepareUsageReportData;
import com.eastedu.report.principal.util.ReportChartUtil;
import com.eastedu.report.principal.util.ReportDescriptionUtil;
import com.eastedu.report.principal.util.ReportTableUtil;
import com.eastedu.template.chart.ChartMetadata;
import com.eastedu.template.util.ChartDataUtil;
import com.eastedu.utils.CollectionUtils;
import com.eastedu.utils.DateUtil;
import com.google.common.collect.Maps;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.text.MessageFormat;
import java.util.*;
import java.util.function.*;
import java.util.stream.Collectors;

/**
 * 教师资源应用分析 - 同步备课
 *
 * @author superman
 */
@Service
public class TeacherPrepareReportDataAssembly implements ReportDataAssembly {

    private final TeacherTeachingActivityResourceUsageMapper teacherTeachingActivityResourceUsageMapper;
    private final TeacherTeachingActivityPrepareResourceUsagePeriodMapper teacherTeachingActivityPrepareResourceUsagePeriodMapper;
    private final PrincipalTeachingActivityConverter principalTeachingActivityConverter;

    /**
     * Instantiates a new Teacher teaching report data assembly.
     *
     * @param teacherTeachingActivityResourceUsageMapper              the teaching activity teacher resource usage mapper
     * @param teacherTeachingActivityPrepareResourceUsagePeriodMapper the teacher teaching activity prepare resource usage period mapper
     * @param principalTeachingActivityConverter                      the principal teaching activity converter
     */
    public TeacherPrepareReportDataAssembly(TeacherTeachingActivityResourceUsageMapper teacherTeachingActivityResourceUsageMapper, TeacherTeachingActivityPrepareResourceUsagePeriodMapper teacherTeachingActivityPrepareResourceUsagePeriodMapper, PrincipalTeachingActivityConverter principalTeachingActivityConverter) {
        this.teacherTeachingActivityResourceUsageMapper = teacherTeachingActivityResourceUsageMapper;
        this.teacherTeachingActivityPrepareResourceUsagePeriodMapper = teacherTeachingActivityPrepareResourceUsagePeriodMapper;
        this.principalTeachingActivityConverter = principalTeachingActivityConverter;
    }

    @Override
    public Map<String, Object> assemble(ReportGenerateParameter parameter, List<String> schoolPeriods, List<String> subjects) {
        DateCodeType codeType = parameter.getCodeType();
        Integer dateCode = parameter.getDateCode();
        Integer termDateCode = codeType.toTerm(dateCode);
        Map<String, Object> map = Maps.newHashMap();


        LambdaQueryWrapper<TeacherTeachingActivityResourceUsagePeriodSubjectPO> wrapper = SqlUtil.queryWrapper(parameter,
                                                                                                               w -> w.and(c ->
                                                                                                                                  c.or(a -> a.eq(CommonDimensionEntity::getCodeType, codeType).eq(CommonDimensionEntity::getDateCode, dateCode))
                                                                                                                                          .or(a -> a.eq(CommonDimensionEntity::getCodeType, DateCodeType.TERM).eq(CommonDimensionEntity::getDateCode, termDateCode)))
                                                                                                                       .eq(TeacherTeachingActivityResourceUsagePeriodSubjectPO::getTeachingActivity, "同步备课"),
                                                                                                               TeacherTeachingActivityResourceUsagePeriodSubjectPO.class);
        // 学届学科维度数据
        List<TeacherTeachingActivityResourceUsagePeriodSubjectPO> pos = teacherTeachingActivityResourceUsageMapper.selectList(wrapper);

        // ============================== 分割线 ===============================
        // 本月同步备课直播参与情况
        List<TeacherTeachingActivityResourceUsagePeriodSubjectPO> nonNullPeriodData = pos.stream().filter(po -> Objects.nonNull(po.getSchoolPeriod())).collect(Collectors.toList());
        this.assemblePartOne(parameter,
                             nonNullPeriodData,
                             map);

        // ============================== 分割线 ===============================
        // 同步备课环节-同步资源使用个数、历史资源使用个数、同步资源使用率

        List<TeacherPrepareUsageReportData> usages = principalTeachingActivityConverter.convertToTeacherPrepareUsageReportData(pos);

        List<TeacherPrepareUsageReportData> monthUsages = usages.stream().filter(data -> DateCodeType.MONTH.equals(data.getCodeType())).collect(Collectors.toList());
        // 2、当月，指标【同步备课环节-同步资源使用个数】≠ 0，则：
        // ---- 1）所有老师的指标【同步备课环节-同步资源使用个数】都 ≠ 0，则输出：本月同届资源使用情况：本月，xx（学届）xx（学科）的学科教师使用同届教学资源最多，为xx个。
        // ---- 2）存在有部分老师的指标【同步备课环节-同步资源使用个数】=0，则输出：本月同届资源使用情况：本月，xx（学届）xx（学科）的学科教师使用同届教学资源最多，为xx个；xx（学届）xx（学科）的学科教师暂未使用同届教学资源。
        map.put("teacher_prepare_followed_resource_usage_number_month_desc", Description.NUMBER.createResourceUsageDescription(monthUsages.stream().filter(data -> Objects.nonNull(data.getSchoolPeriod())).collect(Collectors.toList()),
                                                                                                                               data -> Objects.nonNull(data.getFollowedResourceUseNumber()) && data.getFollowedResourceUseNumber() > 0.0,
                                                                                                                               TeacherPrepareUsageReportData::getFollowedResourceUseNumber));
        // 2、当月，指标【同步备课环节-历史资源使用个数】≠ 0，则：
        // ---- 1）所有老师的指标【同步备课环节-历史资源使用个数】都 ≠ 0，则输出：本月历史资源使用情况：本月，xx（学届）xx（学科）的学科教师使用历史教学资源最多，为xx个。
        // ---- 2）存在有部分老师的指标【同步备课环节-历史资源使用个数】=0，则输出：本月历史资源使用情况：本月，xx（学届）xx（学科）的学科教师使用历史教学资源最多，为xx个；xx（学届）xx（学科）的学科教师暂未使用历史教学资源。
        map.put("teacher_prepare_history_resource_usage_number_month_desc", Description.HISTORY.createResourceUsageDescription(monthUsages.stream().filter(data -> Objects.nonNull(data.getSchoolPeriod())).collect(Collectors.toList()),
                                                                                                                               data -> Objects.nonNull(data.getHistoryResourceUseNumber()) && data.getHistoryResourceUseNumber() > 0.0,
                                                                                                                               TeacherPrepareUsageReportData::getHistoryResourceUseNumber));

        List<ChartMetadata> subjectMetadata = ChartDataUtil.convertSubject(subjects);

        List<ChartMetadata> chartMetadata = ChartDataUtil.convertPeriod(schoolPeriods, Comparator.comparingInt(ChartMetadata::getOrder).reversed());
        // 小学、初中、高中的表格数据（学科、学届维度数据）
        if (StringUtils.equalsIgnoreCase(parameter.getSection(), Section.XX.getCode())) {
            map.put("teacher_prepare_resource_receive_month_table", this.assembleXiaoXueReceiveTable(schoolPeriods, subjects, nonNullPeriodData));
            map.put("teacher_prepare_resource_usage_month_table", this.assembleXiaoXueUsageTable(schoolPeriods, subjects, nonNullPeriodData));
        } else {
            map.put("teacher_prepare_resource_receive_month_table", this.assembleChuGaoTable(schoolPeriods, subjects, nonNullPeriodData));

            // 图表： 📊本月各年级学科同步备课环节使用同步资源个数
            long count = monthUsages.stream().map(TeacherPrepareUsageReportData::getFollowedResourceUseNumber)
                    .filter(Objects::nonNull)
                    .filter(num -> num > 0)
                    .count();
            map.put("teacher_prepare_followed_resource_usage_month_chart_show", count > 0);
            map.put("teacher_prepare_followed_resource_usage_month_chart", ReportChartUtil.chart("本月各年级学科同步备课环节使用同步资源个数",
                                                                                                 subjectMetadata,
                                                                                                 chartMetadata,
                                                                                                 monthUsages,
                                                                                                 TeacherPrepareUsageReportData::getFollowedResourceUseNumber));

            // 图表：📊本月各年级学科同步备课环节使用历史资源个数
            count = monthUsages.stream().map(TeacherPrepareUsageReportData::getHistoryResourceUseNumber)
                    .filter(Objects::nonNull)
                    .filter(num -> num > 0)
                    .count();
            map.put("teacher_prepare_history_resource_usage_month_chart_show", count > 0);
            map.put("teacher_prepare_history_resource_usage_month_chart", ReportChartUtil.chart("本月各年级学科同步备课环节使用历史资源个数",
                                                                                                subjectMetadata,
                                                                                                chartMetadata,
                                                                                                monthUsages,
                                                                                                TeacherPrepareUsageReportData::getHistoryResourceUseNumber));
        }


        List<TeacherPrepareUsageReportData> termUsages = usages.stream().filter(data -> DateCodeType.TERM.equals(data.getCodeType())).collect(Collectors.toList());

        // xx-xx学年（上/下）同步资源教师使用率
        String schoolYear = DateUtil.convertTermToSchoolYear(String.valueOf(termDateCode));
        map.put("teacher_prepare_resource_usage_rate_term_desc", Description.RATE.createResourceUsageDescription(termUsages.stream().filter(data -> Objects.nonNull(data.getSchoolPeriod())).collect(Collectors.toList()),
                                                                                                                 data -> Objects.nonNull(data.getFollowedResourceUseRate()) && data.getFollowedResourceUseRate() > 0.0,
                                                                                                                 TeacherPrepareUsageReportData::getFollowedResourceUseRate));
        // 2023-2024学年（上）各年级学科备课环节同步资源教师使用率
        chartMetadata.sort(Comparator.comparing(ChartMetadata::getOrder));
        long count = termUsages.stream().map(TeacherPrepareUsageReportData::getFollowedResourceUseRate)
                .filter(Objects::nonNull)
                .filter(num -> num > 0)
                .count();
        map.put("teacher_prepare_resource_usage_term_chart_show", count > 0);
        map.put("teacher_prepare_resource_usage_term_chart", ReportChartUtil.chart(schoolYear + "各年级学科备课环节同步资源教师使用率",
                                                                                   subjectMetadata,
                                                                                   chartMetadata,
                                                                                   termUsages,
                                                                                   TeacherPrepareUsageReportData::getFollowedResourceUseRate));
        return map;
    }

    private TableRenderData assembleChuGaoTable(List<String> schoolPeriods, List<String> subjects, List<TeacherTeachingActivityResourceUsagePeriodSubjectPO> pos) {
        List<TeacherTeachingActivityResourceUsagePeriodSubjectPO> record = pos.stream().filter(d -> DateCodeType.MONTH.equals(d.getCodeType())).collect(Collectors.toList());

        Map<String, List<TeacherTeachingActivityResourceUsagePeriodSubjectPO>> subjectGroup = record.stream()
                .collect(Collectors.groupingBy(TeacherTeachingActivityResourceUsagePeriodSubjectPO::getSubjectCode));
        List<String> groups = Arrays.asList("应参与数", "直播参与率", "总参与率（直播+回看）");
        Tables.TableBuilder tableBuilder = ReportTableUtil.getTableBuilder("学科", groups, schoolPeriods);
        // 数据
        if (!CollectionUtils.isEmpty(subjectGroup)) {
            Function<TeacherTeachingActivityResourceUsagePeriodSubjectPO, Number> function = TeacherTeachingActivityResourceUsagePeriodSubjectPO::getFollowedResourceLiveUseRate;
            Function<TeacherTeachingActivityResourceUsagePeriodSubjectPO, Number> function1 = TeacherTeachingActivityResourceUsagePeriodSubjectPO::getFollowedResourceLiveJoinRate;
            fillTableDataRow(tableBuilder, subjects, schoolPeriods,
                             s -> SubjectType.getByCode(s).getName(),
                             (po, col) -> col.equals(po.getSchoolPeriod()),
                             (row) -> subjectGroup.getOrDefault(row, Collections.emptyList())
                                     .stream()
                                     .filter(po -> Objects.nonNull(po.getSchoolPeriod()))
                                     .sorted(Comparator.comparing(TeacherTeachingActivityResourceUsagePeriodSubjectPO::getSchoolPeriod))
                                     .collect(Collectors.toList()),
                             Arrays.asList(
                                     TeacherTeachingActivityResourceUsagePeriodSubjectPO::getFollowedResourceReceiveNumber,
                                     function1.andThen(NumberFormatUtil::formatPercent),
                                     function.andThen(NumberFormatUtil::formatPercent)
                             ));
        }

        // 合计
        Map<String, List<TeacherTeachingActivityResourceUsagePeriodSubjectPO>> collect = record.stream()
                .collect(Collectors.groupingBy(TeacherTeachingActivityResourceUsagePeriodSubjectPO::getSchoolPeriod));
        fillTableTotalRow(tableBuilder, schoolPeriods, collect);
        return tableBuilder.create();
    }

    private TableRenderData assembleXiaoXueReceiveTable(List<String> schoolPeriods, List<String> subjects, List<TeacherTeachingActivityResourceUsagePeriodSubjectPO> pos) {
        List<TeacherTeachingActivityResourceUsagePeriodSubjectPO> record = pos.stream().filter(d -> DateCodeType.MONTH.equals(d.getCodeType())).collect(Collectors.toList());

        Map<String, List<TeacherTeachingActivityResourceUsagePeriodSubjectPO>> group = record.stream()
                .collect(Collectors.groupingBy(TeacherTeachingActivityResourceUsagePeriodSubjectPO::getSchoolPeriod));

        List<String> groups = Arrays.asList("应参与数", "直播参与率", "总参与率（直播+回看）");
        Tables.TableBuilder tableBuilder = ReportTableUtil.getTableBuilder("学届", groups, subjects.stream().map(subject -> SubjectType.getByCode(subject).getName()).collect(Collectors.toList()));
        if (!CollectionUtils.isEmpty(group)) {
            Function<TeacherTeachingActivityResourceUsagePeriodSubjectPO, Number> function = TeacherTeachingActivityResourceUsagePeriodSubjectPO::getFollowedResourceLiveUseRate;
            Function<TeacherTeachingActivityResourceUsagePeriodSubjectPO, Number> function1 = TeacherTeachingActivityResourceUsagePeriodSubjectPO::getFollowedResourceLiveJoinRate;
            fillTableDataRow(tableBuilder, schoolPeriods, subjects,
                             Function.identity(),
                             (po, col) -> col.equals(po.getSubjectCode()),
                             (row) -> group.getOrDefault(row, Collections.emptyList())
                                     .stream()
                                     .filter(po -> Objects.nonNull(po.getSchoolPeriod()))
                                     .sorted(Comparator.comparing(TeacherTeachingActivityResourceUsagePeriodSubjectPO::getSchoolPeriod))
                                     .collect(Collectors.toList()),
                             Arrays.asList(
                                     TeacherTeachingActivityResourceUsagePeriodSubjectPO::getFollowedResourceReceiveNumber,
                                     function1.andThen(NumberFormatUtil::formatPercent),
                                     function.andThen(NumberFormatUtil::formatPercent)
                             ));


        }
        // 合计
        Map<String, List<TeacherTeachingActivityResourceUsagePeriodSubjectPO>> collect = record.stream()
                .collect(Collectors.groupingBy(TeacherTeachingActivityResourceUsagePeriodSubjectPO::getSubjectCode));
        fillTableTotalRow(tableBuilder, subjects, collect);
        return tableBuilder.create();
    }

    private TableRenderData assembleXiaoXueUsageTable(List<String> schoolPeriods, List<String> subjects, List<TeacherTeachingActivityResourceUsagePeriodSubjectPO> pos) {
        List<TeacherTeachingActivityResourceUsagePeriodSubjectPO> record = pos.stream().filter(d -> DateCodeType.MONTH.equals(d.getCodeType())).collect(Collectors.toList());

        Map<String, List<TeacherTeachingActivityResourceUsagePeriodSubjectPO>> group = record.stream()
                .collect(Collectors.groupingBy(TeacherTeachingActivityResourceUsagePeriodSubjectPO::getSchoolPeriod));

        List<String> groups = Arrays.asList("同步备课资源使用个数", "历史备课资源使用个数");
        Tables.TableBuilder tableBuilder = ReportTableUtil.getTableBuilder("学届", groups, subjects.stream().map(subject -> SubjectType.getByCode(subject).getName()).collect(Collectors.toList()));
        if (!CollectionUtils.isEmpty(group)) {
            fillTableDataRow(tableBuilder, schoolPeriods, subjects,
                             Function.identity(),
                             (po, col) -> col.equals(po.getSubjectCode()),
                             (row) -> group.getOrDefault(row, Collections.emptyList())
                                     .stream()
                                     .filter(po -> Objects.nonNull(po.getSchoolPeriod()))
                                     .sorted(Comparator.comparing(TeacherTeachingActivityResourceUsagePeriodSubjectPO::getSchoolPeriod))
                                     .collect(Collectors.toList()),
                             Arrays.asList(
                                     TeacherTeachingActivityResourceUsagePeriodSubjectPO::getFollowedResourceUseNumber,
                                     TeacherTeachingActivityResourceUsagePeriodSubjectPO::getHistoryResourceUseNumber
                             ));


        }
        // 合计
        Map<String, List<TeacherTeachingActivityResourceUsagePeriodSubjectPO>> collect = record.stream()
                .collect(Collectors.groupingBy(TeacherTeachingActivityResourceUsagePeriodSubjectPO::getSubjectCode));

        Rows.RowBuilder rowBuilder = Rows.of("合计").bgColor("c4ccea").textBold().textFontSize(8).center();
        for (String col : subjects) {
            List<TeacherTeachingActivityResourceUsagePeriodSubjectPO> periodGroup = collect.getOrDefault(col, Collections.emptyList());

            rowBuilder.addCell(Cells.create(ReportTableUtil.determinedOutput(periodGroup.stream()
                                                                                     .filter(po -> Objects.nonNull(po.getSchoolPeriod()))
                                                                                     .map(TeacherTeachingActivityResourceUsagePeriodSubjectPO::getFollowedResourceUseNumber)
                                                                                     .filter(Objects::nonNull)
                                                                                     .reduce(0, Integer::sum))));
        }
        for (String col : subjects) {
            List<TeacherTeachingActivityResourceUsagePeriodSubjectPO> periodGroup = collect.getOrDefault(col, Collections.emptyList());

            rowBuilder.addCell(Cells.create(ReportTableUtil.determinedOutput(periodGroup.stream()
                                                                                     .filter(po -> Objects.nonNull(po.getSchoolPeriod()))
                                                                                     .map(TeacherTeachingActivityResourceUsagePeriodSubjectPO::getHistoryResourceUseNumber)
                                                                                     .filter(Objects::nonNull)
                                                                                     .reduce(0, Integer::sum))));
        }
        tableBuilder.addRow(rowBuilder.create());
        return tableBuilder.create();
    }

    private static void fillTableDataRow(Tables.TableBuilder tableBuilder, List<String> rows, List<String> cols,
                                         Function<String, String> headerConverter,
                                         BiPredicate<TeacherTeachingActivityResourceUsagePeriodSubjectPO, String> dataPredicate,
                                         Function<String, List<TeacherTeachingActivityResourceUsagePeriodSubjectPO>> rowDataSupplier,
                                         List<Function<TeacherTeachingActivityResourceUsagePeriodSubjectPO, Object>> colDataSuppliers) {
        for (String row : rows) {
            Rows.RowBuilder rowBuilder = Rows.of(headerConverter.apply(row)).textFontSize(8).center();
            List<TeacherTeachingActivityResourceUsagePeriodSubjectPO> data = rowDataSupplier.apply(row);
            for (Function<TeacherTeachingActivityResourceUsagePeriodSubjectPO, Object> function : colDataSuppliers) {
                for (String col : cols) {
                    rowBuilder.addCell(Cells.create(ReportTableUtil.determinedOutput(data.stream()
                                                                                             .filter(po -> dataPredicate.test(po, col))
                                                                                             .map(function)
                                                                                             .filter(Objects::nonNull)
                                                                                             .findFirst()
                                                                                             .orElse(null))));
                }
            }
            tableBuilder.addRow(rowBuilder.create());
        }
    }

    private static void fillTableTotalRow(Tables.TableBuilder tableBuilder, List<String> cols, Map<String, List<TeacherTeachingActivityResourceUsagePeriodSubjectPO>> record) {
        Rows.RowBuilder rowBuilder = Rows.of("合计").bgColor("c4ccea").textBold().textFontSize(8).center();


        for (String col : cols) {
            List<TeacherTeachingActivityResourceUsagePeriodSubjectPO> periodGroup = record.getOrDefault(col, Collections.emptyList());

            rowBuilder.addCell(Cells.create(ReportTableUtil.determinedOutput(periodGroup.stream()
                                                                                     .filter(po -> Objects.nonNull(po.getSchoolPeriod()))
                                                                                     .map(TeacherTeachingActivityResourceUsagePeriodSubjectPO::getFollowedResourceReceiveNumber)
                                                                                     .filter(Objects::nonNull)
                                                                                     .reduce(0, Integer::sum))));
        }
        for (String col : cols) {
            List<TeacherTeachingActivityResourceUsagePeriodSubjectPO> periodGroup = record.getOrDefault(col, Collections.emptyList());


            int liveJoinNumber = periodGroup.stream()
                    .filter(po -> Objects.nonNull(po.getSchoolPeriod()))
                    .map(TeacherTeachingActivityResourceUsagePeriodSubjectPO::getFollowedResourceLiveJoinNumber)
                    .filter(Objects::nonNull)
                    .reduce(0, Integer::sum);
            int liveReceiveNumber = periodGroup.stream()
                    .filter(po -> Objects.nonNull(po.getSchoolPeriod()))
                    .map(TeacherTeachingActivityResourceUsagePeriodSubjectPO::getFollowedResourceLiveReceiveNumber)
                    .filter(Objects::nonNull)
                    .reduce(0, Integer::sum);

            rowBuilder.addCell(Cells.create(ReportTableUtil.determinedOutput(NumberFormatUtil.formatPercent(liveReceiveNumber == 0 ? 0 : (double) liveJoinNumber / liveReceiveNumber))));
        }

        for (String col : cols) {
            List<TeacherTeachingActivityResourceUsagePeriodSubjectPO> periodGroup = record.getOrDefault(col, Collections.emptyList());
            int receiveNumber = periodGroup.stream()
                    .filter(po -> Objects.nonNull(po.getSchoolPeriod()))
                    .map(TeacherTeachingActivityResourceUsagePeriodSubjectPO::getFollowedResourceReceiveNumber)
                    .filter(Objects::nonNull)
                    .reduce(0, Integer::sum);

            int liveUserNumber = periodGroup.stream()
                    .filter(po -> Objects.nonNull(po.getSchoolPeriod()))
                    .map(TeacherTeachingActivityResourceUsagePeriodSubjectPO::getFollowedResourceLiveUseNumber)
                    .filter(Objects::nonNull)
                    .reduce(0, Integer::sum);

            rowBuilder.addCell(Cells.create(ReportTableUtil.determinedOutput(NumberFormatUtil.formatPercent(receiveNumber == 0 ? 0 : (double) liveUserNumber / receiveNumber))));
        }
        tableBuilder.addRow(rowBuilder.create());
    }

    private void assemblePartOne(ReportGenerateParameter parameter, List<TeacherTeachingActivityResourceUsagePeriodSubjectPO> usages, Map<String, Object> map) {
        DateCodeType codeType = parameter.getCodeType();
        Integer dateCode = parameter.getDateCode();
        // 学届维度数据
        List<TeacherTeachingActivityResourceUsagePeriodPO> pos = teacherTeachingActivityPrepareResourceUsagePeriodMapper.selectList(SqlUtil.queryWrapper(parameter,
                                                                                                                                                         w -> w.eq(CommonDimensionEntity::getCodeType, codeType)
                                                                                                                                                                 .eq(CommonDimensionEntity::getDateCode, dateCode)
                                                                                                                                                                 .eq(TeacherTeachingActivityResourceUsagePeriodPO::getTeachingActivity, "同步备课"),
                                                                                                                                                         TeacherTeachingActivityResourceUsagePeriodPO.class));

        // 文案输出规则：
        //
        //1、本月，指标 【教师应参与同步备课数 】=0，则文案输出：
        //本月同步备课总参与情况：本月，本校教师暂未接收到直播备课资源。
        //
        //2、本月，指标 【教师应参与同步备课数】≠0，则文案输出：
        //本月同步备课总参与情况：本月，xx届、xx届、xx届的教师应参与同步备课分别为xx个、xx个、xx个。
        if (pos.stream()
                .allMatch(data -> Objects.isNull(data.getFollowedResourceReceiveNumber()) || data.getFollowedResourceReceiveNumber() == 0)) {
            map.put("teacher_prepare_resource_receive_number_month_desc", "本月，本校教师暂未接收到直播备课资源。");
            map.put("teacher_prepare_resource_usage_rate_month_desc", null);
        } else {
            // ================================ 本月同步备课直播参与情况 =========================================
            String desc = pos.stream().filter(data -> Objects.nonNull(data.getFollowedResourceReceiveNumber()) && data.getFollowedResourceReceiveNumber() > 0)
                    .sorted(Comparator.comparing(TeacherTeachingActivityResourceUsagePeriodPO::getSchoolPeriod))
                    .map(TeacherTeachingActivityResourceUsagePeriodPO::getSchoolPeriod)
                    .collect(Collectors.joining("、", "本月，", "的教师应参与同步备课{0}为{1}"));

            desc = MessageFormat.format(desc, StringUtils.contains(desc, "、") ? "分别" : "", pos.stream().filter(data -> Objects.nonNull(data.getFollowedResourceReceiveNumber()) && data.getFollowedResourceReceiveNumber() > 0)
                    .sorted(Comparator.comparing(TeacherTeachingActivityResourceUsagePeriodPO::getSchoolPeriod))
                    .map(TeacherTeachingActivityResourceUsagePeriodPO::getFollowedResourceReceiveNumber)
                    .map(String::valueOf)
                    .collect(Collectors.joining("个、 ", "", "个。")));

            List<TeacherPrepareReceiveReportData> receives = principalTeachingActivityConverter.convertToTeacherPrepareReceiveReportData(usages.stream().filter(data -> DateCodeType.MONTH.equals(data.getCodeType())).collect(Collectors.toList()));
            if (receives.stream()
                    .allMatch(data -> Objects.isNull(data.getFollowedResourceLiveJoinRate()) || data.getFollowedResourceLiveJoinRate() == 0)) {
                desc += "各学届各学科教师均未直播参与同步备课。";
            } else {
                Map<String, String> descMap = Maps.newHashMap();
                descMap.put("never", "");
                descMap.put("max", "{0}的学科教师同步备课直播参与率最高，为{1}");
                descMap.put("min", "");
                descMap.put("zero", "");
                BiFunction<Map<String, String>, Function<Number, String>, String> bifunction = ReportDescriptionUtil.minAndMaxDescription(receives,
                                                                                                                                          data -> Objects.nonNull(data.getFollowedResourceLiveJoinRate()) && data.getFollowedResourceLiveJoinRate() > 0.0,
                                                                                                                                          TeacherPrepareReceiveReportData::getFollowedResourceLiveJoinRate);
                desc += bifunction.apply(descMap, NumberFormatUtil::formatPercent);
            }
            map.put("teacher_prepare_resource_receive_number_month_desc", desc);

            // ========================================= 本月同步备课总参与情况 =========================================

            // 文案输出规则：
            //一、本月，指标 【教师应参与同步备课数】=0，则此句话不输出。
            //
            //二、本月，指标 【教师应参与同步备课数 】≠ 0，则：
            //1、指标【教师同步备课总参与率】=0，则文案显示：
            //本月同步备课总参与情况：各学届各学科教师均未参与同步备课。
            //
            //2、指标【教师同步备课总参与率】≠0，则文案输出：
            //本月同步备课总参与情况：xx（学届）xx（学科名称）的学科教师同步备课总参与率最高，为xx.x%。
            //
            //同步备课总参与率：保留1位小数。
            Paragraphs.ParagraphBuilder paragraphBuilder = Paragraphs.of(Texts.of("本月同步备课总参与情况：").bold().create());
            if (receives.stream()
                    .allMatch(data -> Objects.isNull(data.getFollowedResourceLiveUseRate()) || data.getFollowedResourceLiveUseRate() == 0)) {
                map.put("teacher_prepare_resource_usage_rate_month_desc", paragraphBuilder.addText("各学届各学科教师均未参与同步备课。").create());
            } else {
                Map<String, String> descMap = Maps.newHashMap();
                descMap.put("never", "");
                descMap.put("max", "{0}的学科教师同步备课总参与率（直播+回看）最高，为{1}");
                descMap.put("zero", "");
                descMap.put("min", "");
                BiFunction<Map<String, String>, Function<Number, String>, String> bifunction = ReportDescriptionUtil.minAndMaxDescription(receives,
                                                                                                                                          data -> Objects.nonNull(data.getFollowedResourceLiveUseRate()) && data.getFollowedResourceLiveUseRate() > 0.0,
                                                                                                                                          TeacherPrepareReceiveReportData::getFollowedResourceLiveUseRate);
                map.put("teacher_prepare_resource_usage_rate_month_desc", paragraphBuilder.addText(bifunction.apply(descMap, NumberFormatUtil::formatPercent)).create());
            }
        }
    }

    /**
     * The enum Description.
     */
    @Getter
    enum Description {
        /**
         * Number description.
         */
        NUMBER("本月同步备课直播参与情况", () -> {
            Map<String, String> map = Maps.newHashMap();
            map.put("never", "本月，暂无学科教师使用同步备课资源");
            map.put("max", "本月，{0}的学科教师使用同步备课资源最多，为{1}个");
            map.put("min", "");
            map.put("zero", "；{0}的学科教师暂未使用同步备课资源");
            return map;
        }, Object::toString),
        /**
         * History description.
         */
        HISTORY("本月历史资源使用情况", () -> {
            Map<String, String> map = Maps.newHashMap();
            map.put("never", "本月，暂无学科教师使用历史教学资源");
            map.put("max", "本月，{0}的学科教师使用历史教学资源最多，为{1}个");
            map.put("min", "");
            map.put("zero", "");
            return map;
        }, Object::toString),
        /**
         * Rate description.
         */
        RATE("教师同步资源使用率", () -> {
            Map<String, String> map = Maps.newHashMap();
            map.put("never", "截止本月，本学期各学届的学科教师在备课环节均未使用过同步资源");
            map.put("max", "截止本月，本学期{0}学科教师在备课环节的同步资源使用率最高，为{1}");
            map.put("min", "；{0}学科教师在备课环节的同步资源使用率最低，为{1}");
            map.put("zero", "；{0}学科教师在备课环节暂未使用过同步资源");
            return map;
        }, NumberFormatUtil::formatPercent);

        private final String name;
        private final Supplier<Map<String, String>> supplier;
        private final Function<Number, String> format;

        Description(String name, Supplier<Map<String, String>> supplier, Function<Number, String> format) {
            this.name = name;
            this.supplier = supplier;
            this.format = format;
        }

        private String format(Number num) {
            if (Objects.isNull(this.format)) {
                return num.toString();
            }
            return this.format.apply(num);
        }

        /**
         * Create resource usage description string.
         *
         * @param usages    the usages
         * @param predicate the predicate
         * @param function  the function
         * @return the string
         */
        public String createResourceUsageDescription(List<TeacherPrepareUsageReportData> usages,
                                                     Predicate<TeacherPrepareUsageReportData> predicate,
                                                     Function<TeacherPrepareUsageReportData, Number> function) {
            BiFunction<Map<String, String>, Function<Number, String>, String> bifunction = ReportDescriptionUtil.minAndMaxDescription(usages, predicate, function);
            return bifunction.apply(this.getSupplier().get(), this::format);
        }

    }

}
